﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitOfWorkCore.Domain.Abstractions
{
    public abstract class Entity : IEntity
    {
        /// <summary>
        /// 获取所有的主键的值
        /// </summary>
        /// <returns></returns>
        public abstract object[] GetKeys();

        /// <summary>
        /// 重写 Equals - 基于主键数组比较
        /// </summary>
        public override bool Equals(object obj)
        {
            if (obj == null || !(obj is Entity other))
                return false;

            if (ReferenceEquals(this, obj))
                return true;

            if (GetType() != other.GetType())
                return false;

            // 瞬态对象（未持久化）不参与主键比较
            if (IsTransient() || other.IsTransient())
                return false;

            // 比较主键数组
            return GetKeys().SequenceEqual(other.GetKeys());
        }

        /// <summary>
        /// 重写 GetHashCode - 基于主键数组计算
        /// </summary>
        public override int GetHashCode()
        {
            if (IsTransient())
                return base.GetHashCode();

            return GetKeys()
                .Select(k => k?.GetHashCode() ?? 0)
                .Aggregate((x, y) => x ^ y);
        }

        /// <summary>
        /// 判断是否是瞬态对象（主键未持久化）
        /// </summary>
        public virtual bool IsTransient()
        {
            // 默认实现：主键数组全为默认值
            return GetKeys().All(k => k == null || k.Equals(GetDefaultValue(k.GetType())));
        }

        /// <summary>
        /// 获取指定类型的默认值
        /// （值类型返回 new T()，引用类型返回 null）
        /// </summary>
        /// <param name="type">要获取默认值的类型</param>
        /// <returns>类型的默认值</returns>
        private static object GetDefaultValue(Type type)
        {
            return type.IsValueType ? Activator.CreateInstance(type) : null;
        }

        /// <summary>
        /// 重写ToString方法，返回实体标识的标准格式
        /// （格式示例：[Entity: UserRole] Keys = 1,admin）
        /// </summary>
        /// <returns>包含类型名称和主键值的格式字符串</returns>
        public override string ToString()
        {
            return $"[Entity: {GetType().Name}] Keys = {string.Join(",", GetKeys())}";
        }

        /// <summary>
        /// 重载 == 运算符，基于主键值的实体相等性比较
        /// （空引用处理规则：
        ///   - 两个null返回true
        ///   - 单个null返回false
        ///   - 非null对象调用Equals方法）
        /// </summary>
        /// <param name="left">左操作数</param>
        /// <param name="right">右操作数</param>
        /// <returns>是否主键相等</returns>
        public static bool operator ==(Entity left, Entity right)
        {
            // 处理左操作数为null的情况
            if (ReferenceEquals(left, null))
                return ReferenceEquals(right, null); // 双null为true，单null为false

            // 委托给左操作数的Equals方法进行主键值比较
            return left.Equals(right);
        }

        /// <summary>
        /// 重载 != 运算符，基于主键值的实体不等性比较
        /// （等价于 !(left == right)）
        /// </summary>
        /// <param name="left">左操作数</param>
        /// <param name="right">右操作数</param>
        /// <returns>是否主键不等</returns>
        public static bool operator !=(Entity left, Entity right)
        {
            return !(left == right);
        }


        #region 
        private List<IDomainEvent> _domainEvents;
        public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents?.AsReadOnly();

        public void AddDomainEvent(IDomainEvent eventItem)
        {
            _domainEvents = _domainEvents ?? new List<IDomainEvent>();
            _domainEvents.Add(eventItem);
        }

        public void RemoveDomainEvent(IDomainEvent eventItem)
        {
            _domainEvents?.Remove(eventItem);
        }

        public void ClearDomainEvents()
        {
            _domainEvents?.Clear();
        }
        #endregion
    }

    /// <summary>
    /// 单主键实体需继承
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    public abstract class Entity<TKey> : Entity, IEntity<TKey> where TKey : notnull
    {

        /// <summary>
        /// 实体的主键 默认
        /// </summary>
        public virtual TKey Id { get; protected set; }

        /// <summary>
        /// 重写 GetKeys 方法，返回实体的主键
        /// </summary>
        /// <returns></returns>
        public override object[] GetKeys()
        {
            return new object[] { Id };
        }
    }
}
